`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    17:15:29 12/11/2010 
// Design Name: 
// Module Name:    mutex 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module mutex(
	input wire clk,
	input wire reset,
	input wire wr,
	input wire [1:0]pid,
	input wire [7:0]port_id,
	input wire [7:0]out_port,
	output wire l0,l1,l2,l3
);

	reg  updated; 
	reg  [1:0]last_pid;
	reg  [7:0]MutexID[3:0];
	reg  [2:0]Next[3:0];
	reg  [2:0]Next_0;
	reg  [2:0]Next_1;
	reg  [2:0]Next_2;
	reg  [2:0]Next_3;

	localparam [7:0]mutex     = 8'b0000_0000;
	localparam [7:0]end_mutex = 8'b0000_0000;
	
	localparam [2:0]unlock = 3'b111;
	
	localparam [1:0]P0 = 2'b00,
	                P1 = 2'b01,
                   P2 = 2'b10,
                   P3 = 2'b11;

	always @(negedge clk,posedge reset)
	begin
		if (reset)
			begin 
				updated <= 0;
				last_pid <= 0;
				MutexID[0] <= 0;
				MutexID[1] <= 0;
				MutexID[2] <= 0;
				MutexID[3] <= 0;
				Next[0] <= unlock;
				Next[1] <= unlock;
				Next[2] <= unlock;
				Next[3] <= unlock;
			end
		else
			begin
				if (updated)
				begin
					Next[0] <= Next_0;
					Next[1] <= Next_1;
					Next[2] <= Next_2;
					Next[3] <= Next_3;
				end
				if (port_id==mutex && wr)
					begin
						updated  <= 1'b1;
						last_pid <= pid;
						MutexID[pid]<= out_port;
					end
				else 
					updated  <= 1'b0;
			end
	end

	always @(MutexID)
	begin
			Next_0 <= Next[0];
			Next_1 <= Next[1];
			Next_2 <= Next[2];
			Next_3 <= Next[3];
			case (last_pid)
				// Processor ID 0
				P0: 
					if (MutexID[0]==end_mutex) 
						// end mutex, unlock next processor in queue
						Next_0<=unlock;
					else
						// Is the mutex id active for P1?
						if (MutexID[1]==MutexID[0] && Next[1]==unlock)
							// P0 Locked and queued after P1
							Next_1<=0;
						else
							// Is the mutex id active for P2?
							if (MutexID[2]==MutexID[0] && Next[2]==unlock)
								// P0 Locked and queued after P2
								Next_2<=0;
							else
								// Is the mutex id active for P3?
								if (MutexID[3]==MutexID[0] && Next[3]==unlock)
									// P0 Locked and queued after P3
									Next_3<=0;
								else
									// No colision, P0 entered in mutex
									Next_0<=unlock;
				// Processor ID 1
				P1: 
					if (MutexID[1]==end_mutex) 
						// end mutex, unlock next processor in queue
						Next_1<=unlock;
					else
						// Is the mutex id active for P0?
						if (MutexID[0]==MutexID[1] && Next[0]==unlock)
							// P1 Locked and queued after P0
							Next_0<=1;
						else
							// Is the mutex id active for P2?
							if (MutexID[2]==MutexID[1] && Next[2]==unlock)
								// P1 Locked and queued after P1
								Next_2<=1;
							else
								// Is the mutex id active for P3?
								if (MutexID[3]==MutexID[1] && Next[3]==unlock)
									// P1 Locked and queued after P1
									Next_3<=1;
								else
									// No colision, P1 entered in mutex
									Next_1<=unlock;
				// Processor ID 2
				P2: 
					if (MutexID[2]==end_mutex) 
						// end mutex, unlock next processor in queue
						Next_2<=unlock;
					else
						// Is the mutex id active for P0?
						if (MutexID[0]==MutexID[2] && Next[0]==unlock)
							// P2 Locked and queued after P0
							Next_0<=2;
						else
							// Is the mutex id active for P1?
							if (MutexID[1]==MutexID[2] && Next[1]==unlock)
								// P2 Locked and queued after P1
								Next_1<=2;
							else
								// Is the mutex id active for P3?
								if (MutexID[3]==MutexID[2] && Next[3]==unlock)
									// P2 Locked and queued after P3
									Next_3<=2;
								else
									// No colision, P2 entered in mutex
									Next_2<=unlock;
				// Processor ID 3
				P3: 
					if (MutexID[3]==end_mutex) 
						// end mutex, unlock next processor in queue
						Next_3<=unlock;
					else
						// Is the mutex id active for P0?
						if (MutexID[0]==MutexID[3] && Next[0]==unlock)
							// P3 Locked and queued after P0
							Next_0<=3;
						else
							// Is the mutex id active for P1?
							if (MutexID[1]==MutexID[3] && Next[1]==unlock)
								// P3 Locked and queued after P1
								Next_1<=3;
							else
								// Is the mutex id active for P2?
								if (MutexID[2]==MutexID[3] && Next[2]==unlock)
									// P3 Locked and queued after P2
									Next_2<=3;
								else
									// No colision, P3 entered in mutex
									Next_3<=unlock;
			endcase
	end
  
	assign l0 = (Next[1]==0 || Next[2]==0 || Next[3]==0)? 0:1;
	assign l1 = (Next[0]==1 || Next[2]==1 || Next[3]==1)? 0:1;
	assign l2 = (Next[0]==2 || Next[1]==2 || Next[3]==2)? 0:1;
	assign l3 = (Next[0]==3 || Next[1]==3 || Next[2]==3)? 0:1;

endmodule
